// ----------------------------------------
// Filename: Flash.c
// Description: Routines to R/W flash memory
// Author: Anton Jatmiko
// Date: 31 May 2014

#include "Flash.h"

UINT16 CounterByte = 0;
UINT16 CounterPage = 0;
UINT8 CounterSect = 0;

UINT32 CounterNbByte = 0;
UINT32 CounterByteRead = 0;

UINT8 CounterStage = 0;

BOOL Flash_MFULL = 0;

UINT8 Flash_CMD = 0;

BOOL Flash_BusyRD = 0;

// ----------------------------------------
// Flash_Init
//
// Initialise flash
// Input:
//   none
// Output:
//   none
// Conditions:
//   none
void Flash_Init(void)
{
    UINT8 dummy;
    UINT8 content = 0;

    SPI_Init();

    TRISDbits.RD4 = 0;                  // W# OUT
    TRISDbits.RD5 = 0;                  // HOLD# OUT

    FLASH_CSN = 1;                      // release slave

    FLASH_HOLDN = 1;                    // Disable hold
    FLASH_WPROTECTN = 1;                // Disable HPM

    // Write Enable
    FLASH_CSN = 0;                      // Select flash
    while (!SPI_Put(WriteEnableCMD));
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash

    // Write Stat Reg
    FLASH_CSN = 0;                      // Select flash
    while (!SPI_Put(WriteStatCMD));
    while (!SPI_Get(&dummy));
    while (!SPI_Put(StatData));
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash
    __delay_ms(15);                     // Write status cycle time max 15ms

    // Write Enable
    FLASH_CSN = 0;                      // Select flash
    while (!SPI_Put(WriteEnableCMD));
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash

    // Read Byte
    FLASH_CSN = 0;                      // Select flash

    while (!SPI_Put(ReadDataNormSCMD)); // Write CMD
    while (!SPI_Get(&dummy));
    while (!SPI_Put(0));                // Address B2
    while (!SPI_Get(&dummy));
    while (!SPI_Put(0));                // Address B1
    while (!SPI_Get(&dummy));
    while (!SPI_Put(0));                // Address B0
    while (!SPI_Get(&dummy));

    while (!SPI_Put(0));
    while (!SPI_Get(&content));         // read byte

    while (content != 0xFF)
    {
        Flash_WriteAddressCount();
        while (!SPI_Put(0));
        while (!SPI_Get(&content));     // read byte
    }
    FLASH_CSN = 1;                      // Deselect flash


}

// ----------------------------------------
// Flash_Select
//
// Initialise HMI (buttons and LEDs)
// Input:
//   chipSelect is a boolean indicates whether or not to select the flash
// Output:
//   none
// Conditions:
//   assume flash is idle
void Flash_Select(BOOL chipSelect)
{
    FLASH_CSN = chipSelect;
    CounterStage = 0;
}

// ----------------------------------------
// Flash_WriteAddressCount
//
// Count the number of byte in the flash that contains information
// Input:
//   none
// Output:
//   none
// Conditions:
//   none
void Flash_WriteAddressCount(void)
{
    ++CounterByte;
    ++CounterNbByte;
    
    if (CounterByte == 256)
    {
        ++CounterPage;
        CounterByte = 0;
    }
           
    if (CounterPage == 256)
    {
        ++CounterSect;
        CounterPage = 0;
    }

    if (CounterSect >= 4)
        Flash_MFULL = 1; 
}

// ----------------------------------------
// Flash_WriteEnable
//
// Enable flash write
// Input:
//   none
// Output:
//   none
// Conditions:
//   assume write enable command has been sent
void Flash_WriteEnable(void)
{
    UINT8 dummy;

    // Write Enable
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash
}

// ----------------------------------------
// Flash_WriteCMD
//
// Initiate the slave select and send command
// Input:
//   cmd is the command to be sent
// Output:
//   none
// Conditions:
//   assume the flash is idle
void Flash_WriteCMD(UINT8 cmd)
{
    FLASH_CSN = 0;
    Flash_CMD = cmd;
    while (!SPI_Put(cmd));
}

// ----------------------------------------
// Flash_ReadStat
//
// Read the status byte of the flash
// Input:
//   dataPtr is a pointer to the status variable
// Output:
//   none
// Conditions:
//   assume read status command has been sent
void Flash_ReadStat(UINT8 * const dataPtr)
{
    while (!SPI_Put(ReadStatCMD));
    while (!SPI_Get(dataPtr));
    while (!SPI_Put(0));
    while (!SPI_Get(dataPtr));
}

// ----------------------------------------
// Flash_ResetCBR
//
// Reset the counters associated with flash read
// Input:
//   none
// Output:
//   none
// Conditions:
//   none
void Flash_ResetCBR(void)
{
    CounterByteRead = 0;
    CounterStage = 0;
}

// ----------------------------------------
// Flash_ReadByte
//
// Read the flash from address zero
// Input:
//   none
// Output:
//   TRUE if a useful byte is retireved
// Conditions:
//   assume read command has been sent
BOOL Flash_ReadByte(UINT8 * const dataPtr)
{
    UINT8 dummy;

    if ((CounterNbByte != 0) && (CounterByteRead < CounterNbByte))
    {
        if (CounterStage == 0)
        {
            while (!SPI_Get(&dummy));
            while (!SPI_Put(0));        // Address B2
            ++CounterStage;
        }
        else if (CounterStage == 1)
        {
            while (!SPI_Get(&dummy));
            while (!SPI_Put(0));        // Address B1
            ++CounterStage;
        }
        else if (CounterStage == 2)
        {
            while (!SPI_Get(&dummy));
            while (!SPI_Put(0));        // Address B0
            ++CounterStage;
        }
        else if (CounterStage == 3)
        {
            while (!SPI_Get(&dummy));
            while (!SPI_Put(dummy));    // read byte
            ++CounterStage;
        }
        else if (CounterStage == 4)
        {
            while (!SPI_Get(dataPtr));
            ++CounterByteRead;
            return bTRUE;
        }
    }
    else
    {
        FLASH_CSN = 1;                      // Deselect flash
        Flash_ResetCBR();
        Flash_BusyRD = 0;
        Flash_CMD = 0;
    }
    return bFALSE;
}

// ----------------------------------------
// Flash_BulkErase
//
// Erase the entire memory in the flash
// Input:
//   none
// Output:
//   none
// Conditions:
//   none
void Flash_BulkErase(void)
{
    UINT8 dummy;
    UINT8 wipBit = 1;

    // Write Enable
    FLASH_CSN = 0;                      // Select flash
    while (!SPI_Put(WriteEnableCMD));
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash

    // Bulk Erase
    FLASH_CSN = 0;                      // Select flash
    while (!SPI_Put(EraseBulkCMD));
    while (!SPI_Get(&dummy));
    FLASH_CSN = 1;                      // Deselect flash

    // Polling WIP
    FLASH_CSN = 0;
    while ((wipBit & 0x01) && 0x01)
    {
        while (!SPI_Put(ReadStatCMD));
        while (!SPI_Get(&dummy));
        while (!SPI_Put(0));
        while (!SPI_Get(&wipBit));
    }
    FLASH_CSN = 1;

    Flash_MFULL = 0;
    Flash_CMD = 0;
    Flash_BusyRD = 0;
    CounterByte = CounterPage = CounterSect = 0;
    CounterNbByte = CounterByteRead = 0;
}

// ----------------------------------------
// Flash_Write2Byte
//
// Write 2 bytes to the flash
// Input:
//   data is the 2 bytes data to be written
// Output:
//   none
// Conditions:
//   assume page program command has been sent
void Flash_Write2Byte(TUINT16 data)
{
    UINT8 dummy;
    TUINT32 address;

    if (CounterStage == 0)
    {
        address.r.B2 = CounterSect;
        while (!SPI_Get(&dummy));
        while (!SPI_Put(address.r.B2));
        ++CounterStage;
    }
    else if (CounterStage == 1)
    {
        address.r.B1 = (UINT8) CounterPage;
        while (!SPI_Get(&dummy));
        while (!SPI_Put(address.r.B1));
        ++CounterStage;
    }
    else if (CounterStage == 2)
    {
        address.r.B0 = (UINT8) CounterByte;
        while (!SPI_Get(&dummy));
        while (!SPI_Put(address.r.B0));
        ++CounterStage;
    }
    else if (CounterStage == 3)
    {
        while (!SPI_Get(&dummy));
        while (!SPI_Put(data.s.Hi));
        ++CounterStage;
    }
    else if (CounterStage == 4)
    {
        while (!SPI_Get(&dummy));
        while (!SPI_Put(data.s.Lo));
        Flash_WriteAddressCount();
        ++CounterStage;
    }
    else
    {
        while (!SPI_Get(&dummy));
        FLASH_CSN = 1;
        Flash_WriteAddressCount();
        CounterStage = 0;
        Flash_CMD = WriteEnableCMD;
    }
}

// ----------------------------------------
// Flash_CMDHandle
//
// Handle flash command
// Input:
//   cmd is the flash command
// Output:
//   TRUE if a useful byte is retireved
// Conditions:
//   assume flash command has been written
BOOL Flash_CMDHandle(const UINT8 cmd)   // called in sspif isr
{
    switch (cmd)
    {
        case 0x06:    Flash_WriteEnable();          // Write enable
                      if (!Flash_MFULL)
                      {
                          Flash_CMD = PageProgCMD;
                          Flash_WriteCMD(Flash_CMD);
                      }
                      else
                          Flash_CMD = 0;
                      break;
        case 0x02:    Flash_Write2Byte(CelcX10);    // Page prog
                      break;
        case 0xC7:    break;                        // Bulk erase
        case 0x03:    Flash_BusyRD = 1;
                      return Flash_ReadByte(&Flash_RxData); // Read
        default:      break;
    }

    return bFALSE;

}


